package com.agua.enturno.servicios.impl;

import com.agua.enturno.excepciones.ExcepcionDeReglaDelNegocio;
import com.agua.enturno.modelo.agenda.Agenda;
import com.agua.enturno.modelo.agenda.DiaNoLaboral;
import com.agua.enturno.modelo.agenda.JornadaLaboralHabitual;
import com.agua.enturno.modelo.agenda.JornadaLaboralOcasional;
import com.agua.enturno.persistencia.dao.AgendaDAO;
import com.agua.enturno.persistencia.dao.DiaNoLaboralDAO;
import com.agua.enturno.persistencia.dao.JornadaLaboralHabitualDAO;
import com.agua.enturno.persistencia.dao.JornadaLaboralOcasionalDAO;
import com.agua.enturno.servicios.AgendaService;
import com.agua.enturno.util.FiltroDeHorarios;
import com.agua.enturno.vista.dto.*;
import com.agua.enturno.vista.dto.assemblers.*;
import com.agua.enturno.vista.util.AgendaUtil;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

public class AgendaServiceImpl implements AgendaService {

	private AgendaDAO agendaDAO;
	private JornadaLaboralHabitualDAO jornadaLaboralHabitualDAO;
	private JornadaLaboralOcasionalDAO jornadaLaboralOcasionalDAO;
	private DiaNoLaboralDAO diaNoLaboralDAO;
	
	private AgendaAssembler agendaAssembler;
	private JornadaLaboralHabitualAssembler jornadaHabitualAssembler;
	private JornadaLaboralOcasionalAssembler jornadaOcasionalAssembler;
	private HorarioAssembler horarioAssembler;
	private HorarioFullAssembler horarioFullAssembler;
	private DiaNoLaboralAssembler diaNoLaboralAssembler;
	
	
	/*------------------------------------ Public methods ----------------------------------*/

	@Override
	@Transactional
	public AgendaDTO findById(Long id) {
		return agendaAssembler.assemble(agendaDAO.findById(id));
	}
	
	@Override
	@Transactional
	public List<HorarioDTO> findHorarios(Long agendaId) {
		Agenda agenda = agendaDAO.findById(agendaId);
		return horarioAssembler.assemble(agenda.getHorarios());
	}
	
	@Override
	@Transactional
	public DiaDTO findHorariosByDate(Long agendaId, LocalDate fecha) {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		DateTime inicioDelDia = fecha.toDateTimeAtStartOfDay();
		DateTime finDelDia = inicioDelDia.plusDays(1);
		
		return new DiaDTO(fecha, horarioAssembler.assemble(FiltroDeHorarios.ejecutar(agenda.getHorarios(), inicioDelDia, finDelDia)));
	}
	
	@Override
	@Transactional
	public List<HorarioFullDTO> findHorariosFullByInterval(Long agendaId, Interval intervalo) {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		return horarioFullAssembler.assemble(FiltroDeHorarios.ejecutar(agenda.getHorarios(), intervalo));
	}
	
	@Override
	@Transactional
	public DiaDTO findHorariosByDateFilled(Long agendaId, LocalDate fecha) {
		
		// TODO inyectarla por spring
		AgendaUtil agendaUtil = new AgendaUtil();
		return agendaUtil.rellenarDia(findHorariosByDate(agendaId, fecha));
	}
	
	@Override
	@Transactional
	public SemanaDTO findHorariosByWeek(Long agendaId, LocalDate lunes) {
		
		return new SemanaDTO(
			findHorariosByDateFilled(agendaId, lunes),
			findHorariosByDateFilled(agendaId, lunes.plusDays(1)),
			findHorariosByDateFilled(agendaId, lunes.plusDays(2)),
			findHorariosByDateFilled(agendaId, lunes.plusDays(3)),
			findHorariosByDateFilled(agendaId, lunes.plusDays(4)),
			findHorariosByDateFilled(agendaId, lunes.plusDays(5)),
			findHorariosByDateFilled(agendaId, lunes.plusDays(6))
		);
	}
	
	@Override
	@Transactional
	public List<DiaNoLaboralDTO> findDiasNoLaborales(Long agendaId) {
		Agenda agenda = agendaDAO.findById(agendaId);
		return diaNoLaboralAssembler.assemble(agenda.getDiasNoLaborales());
	}
	
	@Override
	@Transactional
	public List<JornadaLaboralHabitualDTO> findJornadasLaboralesHabituales(Long agendaId) {
		Agenda agenda = agendaDAO.findById(agendaId);
		return jornadaHabitualAssembler.assemble(agenda.getJornadasLaboralesHabituales());
	}
	
	@Override
	@Transactional
	public List<JornadaLaboralOcasionalDTO> findJornadasLaboralesOcasionales(Long agendaId) {
		Agenda agenda = agendaDAO.findById(agendaId);
		return jornadaOcasionalAssembler.assemble(agenda.getJornadasLaboralesOcasionales());
	}
	
	@Override
	@Transactional
	public Long agregarJornadaLaboralHabitual(Long agendaId, JornadaLaboralHabitualDTO jornadaHabitualDTO) throws ExcepcionDeReglaDelNegocio {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		JornadaLaboralHabitual jornadaLaboral = jornadaHabitualAssembler.disassemble(jornadaHabitualDTO);
		agenda.agregarJornadaLaboralHabitual(jornadaLaboral);
		
		jornadaLaboral = jornadaLaboralHabitualDAO.saveOrUpdate(jornadaLaboral);
		return jornadaLaboral.getId();
	}
	
	@Override
	@Transactional
	public void quitarJornadaLaboralHabitual(Long agendaId, Long jornadaLaboralHabitualId, String motivo) throws ExcepcionDeReglaDelNegocio {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		JornadaLaboralHabitual jornadaHabitual = jornadaLaboralHabitualDAO.findById(jornadaLaboralHabitualId);
		agenda.quitarJornadaLaboralHabitual(jornadaHabitual, motivo);
	}
	
	@Override
	@Transactional
	public Long agregarJornadaLaboralOcasional(Long agendaId, JornadaLaboralOcasionalDTO jornadaOcasionalDTO) throws ExcepcionDeReglaDelNegocio {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		JornadaLaboralOcasional jornadaLaboral = jornadaOcasionalAssembler.disassemble(jornadaOcasionalDTO);
		agenda.agregarJornadaLaboralOcasional(jornadaLaboral);
		
		jornadaLaboral = jornadaLaboralOcasionalDAO.saveOrUpdate(jornadaLaboral);
		return jornadaLaboral.getId();
	}

	@Override
	@Transactional
	public void quitarJornadaLaboralOcasional(Long agendaId, Long jornadaLaboralOcasionalId, String motivo) throws ExcepcionDeReglaDelNegocio {

		Agenda agenda = agendaDAO.findById(agendaId);
		JornadaLaboralOcasional jornadaOcasional = jornadaLaboralOcasionalDAO.findById(jornadaLaboralOcasionalId);
		agenda.quitarJornadaLaboralOcasional(jornadaOcasional, motivo);
	}
	
	@Override
	@Transactional
	public Long agregarDiaNoLaboral(Long agendaId, DiaNoLaboralDTO diaNoLaboralDTO) throws ExcepcionDeReglaDelNegocio {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		DiaNoLaboral diaNoLaboral = diaNoLaboralAssembler.disassemble(diaNoLaboralDTO);
		agenda.agregarDiaNoLaboral(diaNoLaboral);
		
		diaNoLaboral = diaNoLaboralDAO.saveOrUpdate(diaNoLaboral);
		return diaNoLaboral.getId();
	}

	@Override
	@Transactional
	public void quitarDiaNoLaboral(Long agendaId, Long diaNoLaboralId) throws ExcepcionDeReglaDelNegocio {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		DiaNoLaboral diaNoLaboral = diaNoLaboralDAO.findById(diaNoLaboralId);
		agenda.quitarDiaNoLaboral(diaNoLaboral);
	}
	
	@Override
	@Transactional
	public Long modificarDiaNoLaboral(Long agendaId, Long diaNoLaboralAModificarId, DiaNoLaboralDTO diaNoLaboralModificadoDTO) throws ExcepcionDeReglaDelNegocio {
		
		Agenda agenda = agendaDAO.findById(agendaId);
		DiaNoLaboral diaNoLaboralAModificar = diaNoLaboralDAO.findById(diaNoLaboralAModificarId);
		DiaNoLaboral diaNoLaboralModificado = diaNoLaboralAssembler.disassemble(diaNoLaboralModificadoDTO);
		
		agenda.quitarDiaNoLaboral(diaNoLaboralAModificar);
		
		try {
			agenda.agregarDiaNoLaboral(diaNoLaboralModificado);
			
			diaNoLaboralModificado = diaNoLaboralDAO.saveOrUpdate(diaNoLaboralModificado);
			return diaNoLaboralModificado.getId();
			
		} catch (ExcepcionDeReglaDelNegocio e) {
			agenda.agregarDiaNoLaboral(diaNoLaboralAModificar);
			throw new ExcepcionDeReglaDelNegocio("No se puede modifica el dia no laboral ya que hay otro dia no laboral para la misma fecha");
		}
	}

	/*--------------------------------------------------------------------------------------*/
	/*---------------------------------- Geters and seters ---------------------------------*/

	public void setAgendaDAO(AgendaDAO agendaDAO) {
		this.agendaDAO = agendaDAO;
	}

	public void setJornadaLaboralHabitualDAO(JornadaLaboralHabitualDAO jornadaLaboralHabitualDAO) {
		this.jornadaLaboralHabitualDAO = jornadaLaboralHabitualDAO;
	}

	public void setJornadaLaboralOcasionalDAO(JornadaLaboralOcasionalDAO jornadaLaboralOcasionalDAO) {
		this.jornadaLaboralOcasionalDAO = jornadaLaboralOcasionalDAO;
	}

	public void setDiaNoLaboralDAO(DiaNoLaboralDAO diaNoLaboralDAO) {
		this.diaNoLaboralDAO = diaNoLaboralDAO;
	}
	
	public void setAgendaAssembler(AgendaAssembler agendaAssembler) {
		this.agendaAssembler = agendaAssembler;
	}

	public void setJornadaHabitualAssembler(JornadaLaboralHabitualAssembler jornadaHabitualAssembler) {
		this.jornadaHabitualAssembler = jornadaHabitualAssembler;
	}

	public void setJornadaOcasionalAssembler(JornadaLaboralOcasionalAssembler jornadaOcasionalAssembler) {
		this.jornadaOcasionalAssembler = jornadaOcasionalAssembler;
	}

	public void setHorarioAssembler(HorarioAssembler horarioAssembler) {
		this.horarioAssembler = horarioAssembler;
	}

	public void setHorarioFullAssembler(HorarioFullAssembler horarioFullAssembler) {
		this.horarioFullAssembler = horarioFullAssembler;
	}

	public void setDiaNoLaboralAssembler(DiaNoLaboralAssembler diaNoLaboralAssembler) {
		this.diaNoLaboralAssembler = diaNoLaboralAssembler;
	}
	
	/*--------------------------------------------------------------------------------------*/

}
